/*
 *   Copyright (c) 2018. 刘路 All rights reserved
 *   版权所有 刘路 并保留所有权利 2018.
 *   ===============================================================
 *   这不是一个自由软件！您只能在不用于商业目的的前提下对程序代码进行修改和
 *   使用。不允许对程序代码以任何形式任何目的的再发布。如果项目发布携带作者
 *   认可的特殊 LICENSE 则按照 LICENSE 执行，废除上面内容。请保留原作者信息。
 *   ================================================================
 *   刘路（feedback@zhoyq.com）于 2018. 创建
 *   http://zhoyq.com
 */

package com.zhoyq.helper;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

import java.io.*;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.*;

/**
 * 压缩工具类
 * 实现gzip和zip
 * @author 刘路
 */
public class CompressHelper {

    private static final Integer STREAM_BUFFER_SIZE = 1024;

    public static Logger log = LogManager.getLogger(CompressHelper.class);

    /**
     * 使用gzip对数据进行压缩
     * @param data
     * @return
     */
    public static byte[] gzip(byte[] data){
        try{
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            GZIPOutputStream gos = new GZIPOutputStream(baos);
            int count;
            byte[] buf = new byte[STREAM_BUFFER_SIZE];
            while ((count = bais.read(buf)) != -1) {
                gos.write(buf, 0, count);
            }
            gos.finish();
            gos.flush();
            gos.close();
            baos.flush();
            baos.close();
            bais.close();
            byte[] output = baos.toByteArray();
            return output;
        }catch(Exception e){}
        return null;
    }


    /**
     * 使用gzip对数据进行解压缩
     * @param data
     * @return
     */
    public static byte[] ungzip(byte[] data){
        try{
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            GZIPInputStream gis = new GZIPInputStream(bais);
            int count;
            byte[] buf = new byte[STREAM_BUFFER_SIZE];
            while ((count = gis.read(buf)) != -1) {
                baos.write(buf, 0, count);
            }
            gis.close();
            baos.flush();
            baos.close();
            bais.close();
            byte[] output = baos.toByteArray();
            return output;
        }catch(Exception e){}
        return null;
    }

    /**
     * 使用zip对数据进行压缩
     * @param out
     * @param sourceFile
     * @param base
     * @throws IOException
     */
    public static void zip(ZipOutputStream out, File sourceFile, String base) throws IOException {
        // 如果路径为目录（文件夹）
        if(sourceFile.isDirectory()) {
            // 取出文件夹中的文件（或子文件夹）
            File[] flist = sourceFile.listFiles();
            // 如果文件夹为空，则只需在目的地zip文件中写入一个目录进入点
            if(flist.length == 0){
                out.putNextEntry(  new ZipEntry(base+"/") );
                // 如果文件夹不为空，则递归调用 compress，文件夹中的每一个文件（或文件夹）进行压缩
            } else {
                for(int i=0;i<flist.length;i++) {
                    zip(out,flist[i],base+"/"+flist[i].getName());
                }
            }
        } else {
            // 如果不是目录（文件夹），即为文件，则先写入目录进入点，之后将文件写入zip文件中
            out.putNextEntry( new ZipEntry(base) );
            InputStream is = new FileInputStream(sourceFile);
            int count;
            byte[] buf = new byte[STREAM_BUFFER_SIZE];
            while ((count = is.read(buf)) != -1) {
                out.write(buf, 0, count);
            }
            out.flush();
            is.close();
        }
    }

    /**
     * 将文件夹压缩到zip
     * @param dir
     * @param zip
     */
    public static void zip(File dir,File zip){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream gos = new ZipOutputStream(baos);
        try{
            zip(gos,dir,dir.getName());
            gos.finish();
            gos.close();
            baos.close();
            FileHelper.save(baos.toByteArray(),zip);
        }catch(Exception e){
            e.printStackTrace();
        }
    }

    public static void unzip(File zip,File dir) {
        try{
            ZipFile zipFile = new ZipFile(zip);
            for(Enumeration entries = zipFile.entries(); entries.hasMoreElements();){
                ZipEntry entry = (ZipEntry) entries.nextElement();
                if(entry.isDirectory()){
                    continue;
                }
                String zipEntryName = entry.getName();
                InputStream in = zipFile.getInputStream(entry);
                String outPath = dir.getAbsolutePath() + File.separator + zipEntryName ;
                int end = outPath.lastIndexOf(File.separator);
                if (end != -1) {
                    File file = new File(outPath.substring(0, end));
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                }
                File outFile = new File(outPath);
                OutputStream out = new FileOutputStream(outFile);
                byte[] buf1 = new byte[1024];
                int len;
                while ((len = in.read(buf1)) > 0) {
                    out.write(buf1, 0, len);
                }
                in.close();
                out.close();
            }
            zipFile.close();
        }catch(Exception e){
            log.warn(e.getMessage());
        }
    }

    /**
     * 递归调用压缩 对于ZipEntity
     */
    public static void zip(ZipOutputStream out,List<ZipEntity> zip) throws IOException {
        for(ZipEntity entity:zip){
            if(entity.isFile()){
                out.putNextEntry(new ZipEntry(entity.getName()));
                out.write(entity.getData());
                out.flush();
            }else{
                out.putNextEntry(new ZipEntry(entity.getName()));
                zip(out,entity.getSubFiles());
            }
        }
    }

    /**
     * 压缩提供的实体数据到自己码
     * @param zip
     * @return
     */
    public static byte[] zip(List<ZipEntity> zip){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream gos = new ZipOutputStream(baos);
        byte[] output = null;
        try{
            zip(gos,zip);
            gos.finish();
            gos.close();
            baos.close();
            output = baos.toByteArray();
        }catch(Exception e){ }
        return output;
    }

    /**
     * 压缩提供的数据内容到流中
     * @param os
     */
    public static void compress(OutputStream os,List<ZipEntity> zip){
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ZipOutputStream gos = new ZipOutputStream(baos);
        byte[] output = null;
        try{
            zip(gos,zip);
            gos.finish();
            gos.close();
            baos.close();
            output = baos.toByteArray();
            os.write(output);
        }catch(Exception e){ }
    }

    /**
     * 压缩数据 仅用于压缩 单独的数据 需要对应解压缩方法 读取数据
     * @param data
     * @return
     */
    public static byte[] compress(byte[] data){
        try{
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ZipOutputStream gos = new ZipOutputStream(baos);
            gos.putNextEntry(new ZipEntry("single"));
            int count;
            byte[] buf = new byte[STREAM_BUFFER_SIZE];
            while ((count = bais.read(buf)) != -1) {
                gos.write(buf, 0, count);
            }
            gos.finish();
            gos.flush();
            gos.close();

            byte[] output = baos.toByteArray();
            baos.flush();
            baos.close();
            bais.close();
            return output;
        }catch(Exception e){
            return null;
        }
    }

    /**
     * 对应上边压缩数据 的解压缩方法
     * @param data
     * @return
     */
    public static byte[] decompress(byte[] data){
        try{
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();

            ZipInputStream gis = new ZipInputStream(bais);
            gis.getNextEntry();

            int count;
            byte[] buf = new byte[STREAM_BUFFER_SIZE];
            while ((count = gis.read(buf)) != -1) {
                baos.write(buf, 0, count);
            }
            gis.close();

            byte[] output = baos.toByteArray();
            baos.flush();
            baos.close();
            bais.close();
            return output;
        }catch(Exception e){
            return null;
        }
    }
}
